{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Threshold Paillier Cryptosystem\n", "=============" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A Distributed Decryption for Paillier" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The protocol offers a distributive decryption for Paillier with simulation based security against malicious adversaries without randomness extraction. It is comprised of the following two subprotocols:\n", "\n", "1. The parties produce shares of a value d similarly to the **Damgard-Jurik scheme**\n", "\n", "2. The parties run the distributed decryption algorithm using their shares." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "use $g = 1 + N$ as a generator of the subgroup of $Z^∗_{N^2}$ of order $N$. Encryption of a plaintext $m$ with randomness $r$ is then,\n", "\n", "$$\n", "Enc_N(m, r) = (1+N)^m \\cdot r^N \\mod N^2\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generating a Shared Paillier Decryption Key" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now present our protocol for generating a shared Paillier decryption key. As stated, similarly to **Damgard and Jurik** , we share a decryption exponent as follows:\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "d \\equiv \\left\\{\n", "\\begin{aligned}\n", "0 &\\mod \\phi(N) \\\\\n", "1 &\\mod N\n", "\\end{aligned}\n", "\\right\\}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A distributed generation of a shared Paillier decryption key with passive security:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Input**:\n", "\n", "A public **RSA** modulus $N=pq$ with unknow factorization, additive shares of $\\phi(N)$:\n", "\n", "$$\n", "sk_0 = N-p_0-q_0+1\\\\\n", "sk_1 = -p_1-q_2\n", "$$\n", "\n", "$sk_0$, $sk_1$ hold by $P_0$, and $P_1$ respectively, A public **ElGamel** key $(g,h)$ with secret key shared between the parities. A public Pailler key $N_0>>N^3$ with the secret key hold by $P_0$" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from klefki.numbers.primes import generate_prime\n", "from klefki.numbers import lcm\n", "from klefki.mpc.secret_sharing import additive_share\n", "from klefki.types.algebra.meta import field\n", "import random" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "P, Q = generate_prime(32), generate_prime(32)\n", "N = P * Q\n", "Phi = (P-1) * (Q-1)\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "sk0, sk1 = additive_share(Phi, field(N), 2)\n", "sk0, sk1 = sk0.value, sk1.value" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from klefki.crypto.elgamal import ElGamal" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "x = 42\n", "el = ElGamal(x)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "g, h = el.pubkey" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### **The protocol**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. $P_0$ encrypts $sk_0$ using $N_0$ and sends this to $P_1$" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from klefki.crypto.paillier import Paillier" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "p0, q0 = generate_prime(32**2), generate_prime(32**2)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "N0 = p0 * q0" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "p0 = Paillier(p0, q0)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "e_sk0 = p0.E(sk0)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p0.D(e_sk0).value == sk0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----------------" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. $P_1$ picks $r_1 \\in Z_N$ and $r_\\sigma \\in Z_{2^{logN+k}}$ uniformly at random (for a statistical parameter $κ$ that enables to mask the secret key). $P_1$ computes an encryption of\n", "\n", "$$\n", "(sk_0 + sk_1) \\cdot r_1 + N \\cdot r_\\sigma\n", "$$\n", "\n", "using the homomorphic property of Paillier encryption. This is rerandomized and sent to $P_0$ ." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from klefki.types.algebra.utils import randfield\n", "from klefki.types.algebra.meta import field\n", "from math import log" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "r1 = randfield(field(N)).value\n", "ro = randfield(field(int(2 ** log(N, 2)))).value" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "re = (p0.E(sk1) * e_sk0) ** r1 * p0.E(N) ** ro" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "assert p0.D((p0.E(sk1) * e_sk0) ** r1 * p0.E(N) ** ro) == p0.D(p0.E((sk0+sk1) * r1 + N * ro))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "------------\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. $P_0$ decrypts, thus obtaining plaintext $r_0 ; P_0$ computes $r_0^{−1} \\mod N$ and encrypts this as well as plaintext $sk_0 (r_0^{−1} \\mod N)$ under public-key $N_0$ . Both ciphertexts are sent to $P_1$" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "r0 = p0.D(re)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1270707545493826762" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(~field(N)(r0)).value" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "er0 = p0.E((~field(N)(r0)))" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "esk_r0 = p0.E(sk0 * (~field(N)(r0)).value)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "--------------\n", "4. Based on the encryptions of $r_0^{-1} \\mod N$ and $sk_0(r_0^{-1} \\mod N)$, $P_1$ computes an encryption of:\n", "\n", "\\begin{align}\n", "d&=(sk_0(r_0^{-1} \\mod N) \\cdot r_1 + (r_0^{-1} \\mod N)(sk_1 \\cdot r_1))\\\\\n", "&=r_1(sk_0+sk_1)(r_0^{01} \\mod N)\\\\\n", "&=r_1 \\cdot \\phi(N) \\cdot (r_0^{-1} \\mod N)\n", "\\end{align}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$P_1$ then picks $\\bar{d}_1$ uniformly at random in $Z_{2^3logN+κ}$ , and computes and rerandomizes an encryption of $d + \\bar{d}_1$ . This is sent to $P_0$ and finally, $P_1$ sets its share of d to the integer $-\\bar{d}_1$ ." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "e_d = esk_r0 ** r1 * (er0 ** sk1) ** r1" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "assert p0.D(e_d).value == r1 * (P-1) * (Q-1) * (~field(N)(r0)).value" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "d1_ = randfield(field(int(2 ** (3 * log(N, 2))))).value\n", "\n", "e_d0 = p0.E(d1_) * e_d" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "d1 = -d1_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----------\n", "5. $P_0$ decrypts and obtains $d_0$ : its share of $d$." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "d0 = p0.D(e_d0).value" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Correctness:\n", "\n" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "d = p0.D(e_d).value" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "assert p0.D(e_d).value == d1 + d0" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "assert d % ((P-1) * (Q-1)) == 0\n", "assert d % (P * Q) == 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Performing a Joint Paillier Decryption" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To perform a joint decryption of some ciphertext $c$, both parties need to raise $c$ to their share of the key, $d_0$ or $d_1$ . They then demonstrate that this has been computed correctly using the commitments of the shares. The plaintext is immediately computable from $c^{d_0}$ and $c^{d_1}$ ." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A distributed Paillier decryption with a shared key:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Inputs:** A public Paillier key $N=pq$ with unknown factorization and a ciphertext $C=E_N(m,r)$.\n", "\n", "Party $P_i$ hold its share $d_i$ of the secret decryption exponent, $d=d_0+d_1$ where \n", "\n", "$$\n", "d \\equiv 1 \\mod N \\wedge d \\equiv 0 \\mod \\phi(N)\n", "$$\n", "\n", "Finall, the parties hold commitments to (or rather: ElGamal encryptions of) their key-shares." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**The protocol:**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1. $P_0$ sends $c_0=c^{sk0} \\mod N^2$ to $P_1$. Moreover, $P_0$ demonstrates that this has been done correctly by executing $\\pi_{EQ}$, i.e., that the committed number equals the discrete log of $c_0$ with base c and the plaintext encrypted with ElGamal.\n", "\n", "2. $P_1$ sends $c_1=c^{sk2} \\mod N^2$ to $P_0$. Moreover, $P_1$ demonstrates that this has been done correctly by executing $\\pi_{EQ}$, i.e., that the committed number equals the discrete log of $c_0$ with base c and the plaintext encrypted with ElGamal.\n", "\n", "3. Finally, both parties compute the paintext via Function $L$, $m=L(c_0 \\cdot c_1) \\mod N^2)=((c_0 \\cdot c_1) \\mod N^2 -1) / N)$\n", "\n", "$$\n", "L(u) = \\frac{(u-1)}{n}\n", "$$" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "from klefki.crypto.damgard_jurik import DJPaillier\n", "from klefki.crypto.paillier import L" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "m = 31415926\n", "djp = DJPaillier(P, Q, s=3, strict=True)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "N, G = djp.pubkey" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "r = randfield(G.functor)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "c = djp.E(m, r=r)\n", "assert djp.D(c, priv=d).value == m\n", "assert djp.privkey % lcm((P-1), (Q-1)) == 0\n", "assert djp.privkey % (P * Q) == 1" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "N, G = djp.pubkey" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "c0 = c ** d0\n", "c1 = c ** d1\n", "g0 = G ** d0\n", "g1 = G ** d1" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "assert c0 * c1 == c ** d0 * c ** d1\n", "assert c ** (d0 + d1) == c ** d\n", "assert g0 * g1 == G ** d" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "N::31415926" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "F = field(N, \"N\")\n", "\n", "F(L((c0 * c1).value, N)) * ~F(L((g0 * g1).value, N))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "N::2763856104640951191" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "~F(L((g0 * g1).value, N))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generating a threshold key" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Constructing a threshold key essentially consists of computing a Shamir sharing of this. Our solution consists of two steps: \n", "\n", "* 1) First, compute an additive sharing of $d$. \n", "\n", "* 2) Then, compute Shamir shares of this and decrypt these toward the relevant parties." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\phi(N) \\cdot (\\phi(N)^{-1} \\mod N) \\equiv \\left\\{\n", "\\begin{aligned}\n", "0 &\\mod \\phi(N) \\\\\n", "1 &\\mod N\n", "\\end{aligned}\n", "\\right\\}\n", "$$" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "from klefki.numbers.primes import generate_prime\n", "from klefki.types.algebra.meta import field\n", "from klefki.numbers import invmod\n", "from klefki.mpc.secret_sharing import additive_share\n", "from functools import reduce\n", "from operator import add" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "inv_phi = invmod(Phi, N)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "assert Phi * inv_phi % Phi == 0\n", "assert Phi * inv_phi % N == 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Protocol (the ZK part was ignored):" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* 1. For $1 \\le i \\le k$, party $P_i$ pick $t_i$, uniformly at random from $Z_N$ and $r_i$ uniformly at random from $Z_{N·k·2^n}$ , where $n$ is a security parameter. Each $P_i$ then broadcasts ElGamal two encryptions, $c_{ti}$ of $t_i$ and $c_ri$ of $r_i$. These will be views as sharings of random values, $t=\\sum_{i=1}^{k}t_i$ and $r=\\sum_{i=1}^kr_i$" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "n = 3\n", "k = 6\n", "F1 = field(N, \"Z_N\")\n", "F2 = field(N*k*2**3, \"Z_N.k.2^n\")" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "ts = [randfield(F1) for i in range(k)]\n", "rs = [randfield(F2) for i in range(k)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* 2. The parities obtaining shares $u_i, \\cdots, u_n$ of $t \\cdot \\phi(N)$ as well as encryptions $c_{ui}$ of those shares." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "us = [i*Phi for i in ts]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* 3. For $1 \\le i \\le k$, party $P_i$ broadcast $u_i+N \\cdot r_i$; the parties then decrypt $c_{ui}.(c_{ri})^N$, The parties compute\n", "\n", "$$\n", "v=\\sum_{i=1}^ku_i+N \\cdot r_i\n", "$$" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "from functools import reduce\n", "from operator import add" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "v = sum([us[i] + rs[i] * N for i in range(k)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* 4. Each party locally computes the public value\n", "\n", "$$\n", "\\bar{v} = v^{-1} \\mod N\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thies is then used to compute an additive sharing of $w = t \\cdot \\bar{v}$; $P_i$ locally multiplies $t_i$ by $\\bar{v}$ to compute $w_i$, and all parities raise the encryptions of the $t_i$ to $\\bar{v}$ to opbtain encryption of the $w_i$" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Z_N::7516726862107618122" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "~v" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "ws = [t* (~v) for t in ts]\n", "ds = [w.value * Phi for w in ws]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* 5. Finally, the parities obtaining shares $d_i$ of $d$ along with encryptions $c_{di}$ of the $d_i$.\n", "\n", "The sahred $w=\\sum_{i=1}^kw_i eques$\n", "\n", "\\begin{align}\n", "w=\\left( (t \\cdot \\phi(N) + r \\cdot N)^{-1} \\right) \\cdot t = \\left( \\phi(N)^{-1} \\mod N\\right) + ZN\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "w = sum(ws)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For some integer $z$ of atmost $\\lfloor log_k \\rfloor + \\lfloor logN \\rfloor$ bits. This implies that\n", "\n", "$$\n", "d=( ( \\phi(N)^-1 \\mod N) + zN) \\cdot \\phi(N) = ((\\phi(N)^{-1} \\mod N)\\phi(N) +z\\phi(N)N\n", "$$\n", "\n", "$$\n", "d = w \\cdot Phi(N)\n", "$$" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [], "source": [ "assert Phi * w.value % lcm((P-1), (Q-1)) == 0\n", "assert Phi * w.value % (P * Q) == 1\n", "\n", "assert sum(ds) % lcm((P-1), (Q-1)) == 0\n", "assert sum(ds) % (P * Q) == 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ref:\n", "\n", "* Carmit Hazay, Gert Læssøe Mikkelsen, etc.., Efficient RSA Key Generation and Threshold Paillier in the Two-Party Setting" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }